package ar.edu.caece.compiler;

import static ar.edu.caece.compiler.Main.GRAMMAR_FILE_NAME;
import static ar.edu.caece.compiler.Main.SYMBOLS_FILE_NAME;
import static ar.edu.caece.compiler.helpers.Helper.load;
import static org.apache.commons.logging.LogFactory.getLog;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.logging.Log;

import ar.edu.caece.compiler.error.Error;
import ar.edu.caece.compiler.error.ErrorManager;
import ar.edu.caece.compiler.error.ParsingException;
import ar.edu.caece.compiler.grammar.AbstractTokenVisitorFactory;
import ar.edu.caece.compiler.grammar.Recognizer;
import ar.edu.caece.compiler.grammar.attributes.Attribute;
import ar.edu.caece.compiler.grammar.attributes.tree.NodeListaBloques;
import ar.edu.caece.compiler.grammar.factories.PROGRAMAFactory;
import ar.edu.caece.compiler.helpers.Helper;
import ar.edu.caece.compiler.model.Grammar;
import ar.edu.caece.compiler.model.LexicalAnalyzer;
import ar.edu.caece.compiler.symbol.SymbolTable;

public class Main {

	public final static String PROGRAM_FILE_NAME = "test.leb";
	
	public final static String SYMBOLS_FILE_NAME = "symbols.txt";
	
	public final static String NODO_FILE_NAME = "nodos.csv";
	
	public final static String TRANSITIONS_FILE_NAME = "aristas.csv";
	
	public final static String GRAMMAR_FILE_NAME = "grammar.txt";	

	public final static String[] rutinas = {"readln","multE","divE","sumaE","restaE",
													 "multN","divN","sumaN","restaN","convE2N"};

	/**
	 * M�todo que simula al Analizador sint�ctico, pidiendo al analizador l�xico el pr�ximo token, hasta obtener el token de fin de archivo.
	 * @param args
	 * @throws IOException 
	 */
	public static void main(String[] args) throws IOException {
		
		Log logger = getLog(Main.class);
		LexicalAnalyzer la = null;

		Grammar grammar;
		SymbolTable symbolTable = new SymbolTable(SYMBOLS_FILE_NAME);
		
		grammar = new Grammar(GRAMMAR_FILE_NAME, symbolTable);
		
		ErrorManager errorManager = ErrorManager.getInstance();
		errorManager.setGrammar(grammar);
		
		String configDir = "";
		if(args.length==0) {
			System.out.println("Error. Debe llamarse al al programa con el directorio de configuraci�n como argumento.");
		} else {
			if(args[0]!=null) {
				configDir = args[0];
				if(!args[0].endsWith("\\")) configDir = configDir+"\\";
			}
		}
		
		try {
			
			logger.info("Start");
			
			la = new LexicalAnalyzer(new SymbolTable(configDir+SYMBOLS_FILE_NAME), configDir+PROGRAM_FILE_NAME, configDir+NODO_FILE_NAME, configDir+TRANSITIONS_FILE_NAME);
			
			logger.info("End");
			try {
				SyntacticAnalysis(la,configDir);
			} catch(ParsingException e) {
				System.out.println("Error de parseo en " + e.getReceived().getToken().getPos() + ": " + e.toString());
			}
			logger.debug("Programa procesado");

		}
		catch(Exception e) {
			logger.error("Error al procesar el archivo. Programa no reconocido.",e);
			logger.debug("Error al procesar el archivo. Programa no reconocido.");
		}

	}
	
	private static void SyntacticAnalysis(LexicalAnalyzer la,String configDir) throws Exception {
		Log logger = getLog(Main.class);
		Recognizer rec;
		
		AbstractTokenVisitorFactory initialFactory = PROGRAMAFactory.getInstance(la);
		initialFactory.init();
		
		//--------------------------------------------------------------------------
		// my debug
		
		/*
		logger.debug("--------------------------------");
		logger.debug("Factory: " + initialFactory);
		logger.debug("--------------------------------");
		ar.edu.caece.compiler.grammar.TokenRecognizer token = AbstractTokenVisitorFactory.nextToken();
		while(token.getClass() != ar.edu.caece.compiler.grammar.terminals.EofToken.class) {
			logger.debug("Token: " + token);
			token = AbstractTokenVisitorFactory.nextToken();
		}
		logger.debug("--------------------------------");
		*/
		//--------------------------------------------------------------------------
		
		rec = AbstractTokenVisitorFactory.nextToken().execute(initialFactory.makeVisitor());
		logger.debug(rec.getRepresentation());
		NodeListaBloques raiz = (NodeListaBloques) rec.getAttribute(Attribute.ARBOL_SINTETIZADO);
		raiz.propagateRoot();
		raiz.prepararTemporales();
		logger.debug(raiz);
	
		List<ar.edu.caece.compiler.error.Error> errors = ErrorManager.getInstance().getErrors(); 
		for(Error error : errors) {
			logger.debug(error.toString());
		}
		if(errors.size()==0) {
			generarCodigo(configDir, raiz);
			logger.debug("Código generado en archivo 'generado.asm'");
		}
		else logger.debug("El programa contiene errores. No se ha generado el c�digo.");
		
				
		
	}

	private static void generarCodigo(String configDir, NodeListaBloques raiz)
			throws IOException {
		StringBuilder builder = new StringBuilder();
		
		builder.append(raiz.generateDeclaration()).append(raiz.generateCode());
		
		incluirRutinas(builder);
		
		Helper.writeFile(configDir+"generado.asm",builder.toString());
		
	}

	private static void incluirRutinas(StringBuilder builder)
			throws IOException {
		List<String> lines = new ArrayList<String>();
				
		for(String rutina : rutinas) {
			if(builder.indexOf(rutina)!=-1) {
				lines.addAll(load("rutina"+rutina+".asm"));
				lines.add(System.getProperty("line.separator"));
			}
		}
		
		for(String line : lines) {
			builder.append(line).append(System.getProperty("line.separator"));
		}
		lines = new ArrayList<String>();
		if(builder.indexOf("writeSTR")!=-1 || builder.indexOf("writeNUM")!=-1) {
			lines.addAll(load("rutinaWriteNum.asm"));
			lines.add(System.getProperty("line.separator"));
			lines.addAll(load("rutinaWriteCRLF.asm"));
			lines.add(System.getProperty("line.separator"));
		}
		for(String line : lines) {
			builder.append(line).append(System.getProperty("line.separator"));
		}
	}
}
